﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Windows.Forms;

namespace NetworkTools {
    public partial class SubnetCaclulatorControl : UserControl {
        private readonly Stack<int> _recalculateEnabled = new Stack<int>();
        
        public SubnetCaclulatorControl() {
            InitializeComponent();

            _recalculateEnabled.Push(1);
            byte[] addressBytes = GetDefaultAddressBytes();
            if (addressBytes != null) {
                SetAddress(addressBytes);
            } else {
                SetAddress(new byte[] { 192, 168, 0, 1 });
            }

            _networkClassA.Checked = true;
            _networkClassA_CheckedChanged(this, null);
            _recalculateEnabled.Pop();

            Recalculate();
        }

        private void Recalculate() {
            if (_recalculateEnabled.Count == 0) {
                _maskBits.Text = GetMaskBits();
                _maximumSubnets.Text = GetMaximumSubnets();
                _hostsPerSubnet.Text = GetHostsPerSubnet();
                _hexIpAddress.Text = GetHexIpAddress();
                _wildcardMask.Text = GetWildcardMask();
                _hostAddressRange.Text = GetHostAddressRange();
                _subnetId.Text = GetSubnetId();
                _broadcastAddress.Text = GetBroadcastAddress();
                _subnetBitmap.Text = GetSubnetBitmap();
            }
        }

        private string GetMaskBits() {
            return SubnetCalculator.GetMaskBitsFromSubnetBits(GetNetworkClass(), int.Parse(_subnetBits.Text)).ToString();
        }

        private string GetMaximumSubnets() {
            return SubnetCalculator.GetMaximumSubnetsFromSubnetBits(GetNetworkClass(), int.Parse(_subnetBits.Text)).ToString();
        }

        private string GetHostsPerSubnet() {
            return SubnetCalculator.GetHostsPerSubnetFromSubnetBits(GetNetworkClass(), int.Parse(_subnetBits.Text)).ToString();
        }

        private string GetFirstOctetRange() {
            return SubnetCalculator.GetFirstOctetRange(GetNetworkClass());
        }

        private string GetHexIpAddress() {
            return SubnetCalculator.GetHexIpAddress(GetAddressBytes());
        }

        private string GetWildcardMask() {
            return SubnetCalculator.GetWildcardMaskFromSubnetBits(GetNetworkClass(), int.Parse(_subnetBits.Text));
        }

        private string GetHostAddressRange() {
            return SubnetCalculator.GetHostAddressRangeFromSubnetBits(GetNetworkClass(), GetAddressBytes(), int.Parse(_subnetBits.Text));
        }

        private string GetSubnetId() {
            return SubnetCalculator.GetSubnetId(GetNetworkClass(), GetAddressBytes());
        }

        private string GetBroadcastAddress() {
            return SubnetCalculator.GetBroadcastAddressFromSubnetBits(int.Parse(_subnetBits.Text));
        }

        private string GetSubnetBitmap() {
            return SubnetCalculator.GetSubnetBitmap(GetNetworkClass(), int.Parse(_subnetBits.Text));
        }

        private NetworkClass GetNetworkClass() {
            if (_networkClassA.Checked) {
                return NetworkClass.A;
            }
            if (_networkClassB.Checked) {
                return NetworkClass.B;
            }
            if (_networkClassC.Checked) {
                return NetworkClass.C;
            }
            throw new Exception("Invalid network class selected.");
        }

        private void SetAddress(byte[] addressBytes) {
            _ipAddress.Text = string.Format("{0}.{1}.{2}.{3}",
                addressBytes[0],
                addressBytes[1],
                addressBytes[2],
                addressBytes[3]);
        }

        private byte[] GetAddressBytes() {
            string[] parts = _ipAddress.Text.Split('.');
            byte[] result = new byte[4];
            result[0] = Convert.ToByte(parts[0].Trim(), 10);
            result[1] = Convert.ToByte(parts[1].Trim(), 10);
            result[2] = Convert.ToByte(parts[2].Trim(), 10);
            result[3] = Convert.ToByte(parts[3].Trim(), 10);
            return result;
        }

        private byte[] GetDefaultAddressBytes() {
            foreach (var nic in NetworkInterface.GetAllNetworkInterfaces()) {
                IPInterfaceProperties ipProperties = nic.GetIPProperties();
                foreach (UnicastIPAddressInformation ipAddressInformation in ipProperties.UnicastAddresses) {
                    IPAddress address = ipAddressInformation.Address;
                    if (address.AddressFamily == AddressFamily.InterNetwork) {
                        return address.GetAddressBytes();
                    }
                }
            }
            return null;
        }

        private void _networkClassA_CheckedChanged(object sender, EventArgs e) {
            if (_networkClassA.Checked) {
                _recalculateEnabled.Push(1);
                ClearAllDropDowns();
                _subnetMask.Items.AddRange(SubnetCalculator.SUBNETMASK_CLASS_A);
                _subnetBits.Items.AddRange(SubnetCalculator.SUBNETBITS_CLASS_A.ToObjects());
                _maximumSubnets.Items.AddRange(SubnetCalculator.MAXSUBNETS_CLASS_A.ToObjects());
                _maskBits.Items.AddRange(SubnetCalculator.MASKBITS_CLASS_A.ToObjects());
                _hostsPerSubnet.Items.AddRange(SubnetCalculator.HOSTSPERSUBNET_CLASS_A.ToObjects());
                _subnetMask.Text = "255.0.0.0";
                _firstOctetRange.Text = GetFirstOctetRange();
                _subnetBits.Text = "0";
                _recalculateEnabled.Pop();

                Recalculate();
            }
        }

        private void _networkClassB_CheckedChanged(object sender, EventArgs e) {
            if (_networkClassB.Checked) {
                _recalculateEnabled.Push(1);
                ClearAllDropDowns();
                _subnetMask.Items.AddRange(SubnetCalculator.SUBNETMASK_CLASS_B);
                _subnetBits.Items.AddRange(SubnetCalculator.SUBNETBITS_CLASS_B.ToObjects());
                _maximumSubnets.Items.AddRange(SubnetCalculator.MAXSUBNETS_CLASS_B.ToObjects());
                _maskBits.Items.AddRange(SubnetCalculator.MASKBITS_CLASS_B.ToObjects());
                _hostsPerSubnet.Items.AddRange(SubnetCalculator.HOSTSPERSUBNET_CLASS_B.ToObjects());
                _subnetMask.Text = "255.255.0.0";
                _firstOctetRange.Text = GetFirstOctetRange();
                _subnetBits.Text = "0";
                _recalculateEnabled.Pop();

                Recalculate();
            }
        }

        private void _networkClassC_CheckedChanged(object sender, EventArgs e) {
            if (_networkClassC.Checked) {
                _recalculateEnabled.Push(1);
                ClearAllDropDowns();
                _subnetMask.Items.AddRange(SubnetCalculator.SUBNETMASK_CLASS_C);
                _subnetBits.Items.AddRange(SubnetCalculator.SUBNETBITS_CLASS_C.ToObjects());
                _maximumSubnets.Items.AddRange(SubnetCalculator.MAXSUBNETS_CLASS_C.ToObjects());
                _maskBits.Items.AddRange(SubnetCalculator.MASKBITS_CLASS_C.ToObjects());
                _hostsPerSubnet.Items.AddRange(SubnetCalculator.HOSTSPERSUBNET_CLASS_C.ToObjects());
                _subnetMask.Text = "255.255.255.0";
                _firstOctetRange.Text = GetFirstOctetRange();
                _subnetBits.Text = "0";
                _recalculateEnabled.Pop();

                Recalculate();
            }
        }

        private void ClearAllDropDowns() {
            _subnetMask.Items.Clear();
            _subnetBits.Items.Clear();
            _maximumSubnets.Items.Clear();
            _maskBits.Items.Clear();
            _hostsPerSubnet.Items.Clear();
        }

        private void _ipAddress_Validated(object sender, EventArgs e) {
            Recalculate();
        }

        private void _subnetMask_SelectedIndexChanged(object sender, EventArgs e) {
            _recalculateEnabled.Push(1);
            _subnetBits.Text = SubnetCalculator.GetSubnetBitsFromSubnetMask(GetNetworkClass(), _subnetMask.Text).ToString();
            _recalculateEnabled.Pop();
            Recalculate();
        }

        private void _subnetBits_SelectedIndexChanged(object sender, EventArgs e) {
            _recalculateEnabled.Push(1);
            // all calulations are keyed off this value so skip any calculations.
            _recalculateEnabled.Pop();
            Recalculate();
        }

        private void _maskBits_SelectedIndexChanged(object sender, EventArgs e) {
            _recalculateEnabled.Push(1);
            _subnetBits.Text = SubnetCalculator.GetSubnetBitsFromMaskBits(GetNetworkClass(), int.Parse(_maskBits.Text)).ToString();
            _recalculateEnabled.Pop();
            Recalculate();
        }

        private void _maximumSubnets_SelectedIndexChanged(object sender, EventArgs e) {
            _recalculateEnabled.Push(1);
            _subnetBits.Text = SubnetCalculator.GetSubnetBitsFromMaximumSubnets(GetNetworkClass(), int.Parse(_maximumSubnets.Text)).ToString();
            _recalculateEnabled.Pop();
            Recalculate();
        }

        private void _hostsPerSubnet_SelectedIndexChanged(object sender, EventArgs e) {
            _recalculateEnabled.Push(1);
            _subnetBits.Text = SubnetCalculator.GetSubnetBitsFromHostsPerSubnet(GetNetworkClass(), int.Parse(_hostsPerSubnet.Text)).ToString();
            _recalculateEnabled.Pop();
            Recalculate();
        }
    }
}
